package wator.view.commandline;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Observable;
import java.util.Observer;
import java.util.Vector;

import wator.model.Agent;
import wator.model.AgentConfiguration;
import wator.model.AvailableAgents;
import wator.model.Population;
import wator.model.Simulation;
import wator.model.SimulationThread;
import wator.util.BatchSimulationConfiguration;
import wator.util.GlobalConfiguration;
import wator.util.Tools;
import wator.view.UI;
import wator.view.swing.SwingUI;

//import wator.util.FileConfiguration;

/**
 * ... initialize <code>singleton</code> in
 * {@link wator.view.UI}.
 * 
 * @author Alioune Schurz----------------------------------------------------------------------------------------
 */

public class CommandlineUI extends UI implements Observer {

	static private CommandlineUI singleton = new CommandlineUI();
	private BatchSimulationConfiguration batchConf=new BatchSimulationConfiguration();
	boolean simStarted=false;
	BufferedReader br;
	
	private int round;
	private int predators;
	private int preys;
	private String batchName;
	
	/* (non-Javadoc)
	 * @see wator.view.UI#initialize()
	 */
		
	@Override
	public void initialize() {
        Simulation.getInstance().addObserver(this);	
        readUserInput();
	}
	
	static public CommandlineUI getInstance() {
		return singleton;
	}
	
	public void readUserInput(){
		String command=new String();
		br = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("Welcome on wator-cli !" +
				"\nType `help` or `h`for help on the commands,species and strategies" +
				"\nType `species` or `spe` to list the species" +
				"\nType `strategies` or `str` ro list the strategies");
		
		if(GlobalConfiguration.debugCommands.length>0)
		{
			System.out.println("THE FOLLOWING COMMANDS WHERE INSERTED:\n(To change it, modify the `GlobalConfiguration.debugCommands` array)");
			for(String com : GlobalConfiguration.debugCommands)
				System.out.println("\t-"+com);
		}
			
		for(String com : GlobalConfiguration.debugCommands)
			handleCommand(com);
		
		while(true)
		{
		      try {
		    	 System.out.print("wator-cli>");
		         command = br.readLine();
		         
		      } catch (IOException ioe) {
		         System.out.println("IO error trying to read the command!");
		         continue;
		      }	
		      handleCommand(command);
		}
	}
	
	
	private boolean checkargs(String[] args,String comType,int argc)
	{
		if((args.length==0 && argc!=0) || (args.length!=0 && args.length!=argc+1))
		{
			System.out.println("Wrong number of arguments for command `"+comType+"`. Expected "+argc+".");
			return false;
		}
		return true;
		
	}
	private void strategyHelp()
	{
		 System.out.println(
				  "****************\n"+
				  "List of strategies:\n" +
				  "\t-NoMovement\n" +
				  "\t-Random(Reach)\n"+
				  "\t-InertiaRandom(onSpeed,Inertia Weight,Max. Velocity)\n" +
				  "\t-WeightedForceVector(onSpeed,Flocking,Predator Avoid.,Prey Attraction,Energy Attraction,Separation Range,Inertia,Max. Velocity)\n"
				  );
	}
	private void commandHelp()
	{
		System.out.println(
				  "****************\n" +
				  "List of commands:\n" +
				  "\t-help,h\t\t\tDisplays help on commands, species and strategies.\n"+
				  "\t-species,spe\t\tDisplays help on species.\n"+
				  "\t-strategies,str\t\tDisplays help on strategies.\n\n"+
				  "\t-ls\t\t\tDisplays all the species and selected species, with their initial populations.\n" +
				  "\t-ll\t\t\tSame as ls but also outputs the strategies and their parameters.\n" +
				  "\t-select,sel\t\tSelects a specie. Example: select predator Shark OR select prey Fish \n" +
				  "\t-set\t\t\tSets a parameter for a specie.\n" +
				  "\t\t\t\tExamples:\n" +
				  "\t\t\t\t set Shark {population,pop} 50\n" +
				  "\t\t\t\t set Shark {strategy,str} Random(5)\n" +
				  "\t-start\t\t\tStarts the simulation.\n" +
				  "\t\t\t\tUsage:\n" +
				  "\t\t\t\tstart stravePredMin stravePredMax breedPredMin breedPredMax breedPreyMin breedPreyMax stepStarvePred stepBreedPred stepBreedPrey\n" +
				  "\t-exit\t\t\tExits the wator-cli.\n");
	}
	private void specieHelp()
	{
		  System.out.println(
				  "****************\n"+
				  "List of species:\n" +
				  "\t-Shark\n" +
				  "\t-EnergyShark\n"+
				  "\t-Fish\n" +
				  "\t-BigFish\n");
	}
	private void handleCommand(String command)
	{
		
		  Vector<String> valid_command=new Vector<String>();
		  valid_command.add("set");
		  valid_command.add("help");
		  valid_command.add("h");
		  valid_command.add("species");
		  valid_command.add("spe");
		  valid_command.add("strategies");
		  valid_command.add("str");
		  valid_command.add("exit");
		  valid_command.add("set");
		  valid_command.add("select");
		  valid_command.add("sel");
		  valid_command.add("ls");
		  valid_command.add("ll");
		  valid_command.add("start");

		  command=command.trim();
		  if(command.equals(""))
			  return;
		  
		  String comType=command;
		  String[] args=new String[0];
		  
		  if(command.contains(" "))
		  {
			  args=command.split(" ");
			  comType=args[0];
		  }
		  if(!valid_command.contains(comType))
		  {
			  System.out.println("Invalid command `"+comType+"`.");
			  return;
		  }  
		  if(comType.equals("exit"))
		  {
			  if(!checkargs(args,comType,0))
				  return;
			  System.out.println("Exiting wator-cli ..");
	    	  System.exit(0);
		  }
		  if(comType.equals("help") || comType.equals("h"))
		  {
			  if(!checkargs(args,comType,0))
				  return;
			  commandHelp();
			  specieHelp();
			  strategyHelp();
		  }
		  if(comType.equals("species") || comType.equals("spe"))
		  { 
			  if(!checkargs(args,comType,0))
				  return;
			  specieHelp();
		  }
		  if(comType.equals("strategies") || comType.equals("str"))
		  {
			  if(!checkargs(args,comType,0))
				  return;
			  strategyHelp();
		  }
		  if(comType.equals("ls"))
		  {
			  if(!checkargs(args,comType,0))
				  return;
			  AgentConfiguration[] confs= AvailableAgents.getInstance().getConfigurations();
			  for(AgentConfiguration conf : confs)
			  {
				String agentType=Tools.extractAgentType(conf.getTemplate().toString());
				if(batchConf.isSelected(agentType)) System.out.print("[X]");
				else System.out.print("[ ]");
				System.out.println(agentType+" "+conf.getInitialPopulationSize());
			  }
		  }
		  if(comType.equals("ll"))
		  {
			  if(!checkargs(args,comType,0))
				  return;
			  AgentConfiguration[] confs= AvailableAgents.getInstance().getConfigurations();
			  for(AgentConfiguration conf : confs)
			  {
				String agentType=Tools.extractAgentType(conf.getTemplate().toString());
				if(batchConf.isSelected(agentType)) System.out.print("[X]");
				else System.out.print("[ ]");
				System.out.println(agentType+" "+conf.getInitialPopulationSize());
				if(batchConf.isSelected(agentType)) 
					System.out.print(	"\t\t["+conf.getTemplate().getMovementStrategy().getName()+" "+
										conf.getTemplate().getMovementStrategy().getParameters()+"]");
				System.out.print("\n");
			  }
		  }
		  if(comType.equals("select") || comType.equals("sel"))
		  {
			  if(!checkargs(args,comType,2))
				  return;
	    	  String role=args[1]; 
	    	  String specie=args[2];
	    	  if(role.equals("predator") || role.equals("pred"))
	    		  batchConf.selectPredator(specie+"Agent");
	    	  else if(role.equals("prey"))
	    		  batchConf.selectPrey(specie+"Agent");
	    	  else
	    		  System.out.println("`"+role+"` does not refer to any valid role. Role is either predator or prey");
		  }
		  if(comType.equals("set"))
		  {
			  if(!checkargs(args,comType,3))
				  return;
			  
	    	  String specie=args[1];
	    	  String key=args[2];
	    	  String value=args[3];
	    	  
	    	  batchConf.setParameter(specie+"Agent", key, value);
		  }
		  if(comType.equals("start"))
		  {
			  if(batchConf.prey.equals(""))
			  {
				  System.out.println("Unable to start simulation. Please select a prey...");
				  return;
			  }
			  if(batchConf.predator.equals(""))
			  {
				  System.out.println("Unable to start simulation. Please select a predator...");
				  return;
			  }
				  
			  if(!checkargs(args,comType,9))
				  return;
			  
	    	  batchConf.setBatchIntervals(	new Integer(args[1].trim()), new Integer(args[2].trim()),
	    			  						new Integer(args[3].trim()), new Integer(args[4].trim()), 
	    			  						new Integer(args[5].trim()), new Integer(args[6].trim()));
	    	  batchConf.setBatchSteps(new Integer(args[7].trim()), new Integer(args[8].trim()), new Integer(args[9].trim()));
	    	  
	    	  String param=new String();
	    	  
	  		  br = new BufferedReader(new InputStreamReader(System.in));
	  		  System.out.print("Number of rounds ?>");
	  		  try {
				param = br.readLine();
				batchConf.setNumberOfRounds(new Integer(param.trim()));
			  } catch (IOException e) {
				System.out.println("Could not read your input.");
				return;
			  }
	  		  System.out.print("Number of simulations ?>");
	  		  try {
				param = br.readLine();
				batchConf.setNumberOfSimulations(new Integer(param.trim()));
			  } catch (IOException e) {
				System.out.println("Could not read your input.");
				return;
			  }
			  System.out.print("Output file prefix ?>");
	  		  try {
				param = br.readLine();
				batchName=param.trim();
			  } catch (IOException e) {
				System.out.println("Could not read your input.");
				return;
			  }
			  saveBatchConfiguration();
			  simulationScheduler();
		  }
	}
	/* (non-Javadoc)
	 * @see wator.view.UI#show()
	 */
	@Override
	public void show() {
		
	}
	
	private void simulationScheduler()
	{
		
		 try {
			 //open files for results
			 FileOutputStream fout = new FileOutputStream("results/batch/"+batchName+".dat");
			 PrintStream stream=new PrintStream(fout);
			  
			 float prog=0;
			 float total=	(batchConf.starvePredatorTo-batchConf.starvePredatorFrom+1)/batchConf.starvePredatorStep
						*	(batchConf.breedPredatorTo-batchConf.breedPredatorFrom+1)/batchConf.breedPredatorStep
						*	(batchConf.breedPreyTo-batchConf.breedPreyFrom+1)/batchConf.breedPreyStep
						*	batchConf.numberOfSimulations;
			
			for(int stPred=batchConf.starvePredatorFrom;stPred<=batchConf.starvePredatorTo;stPred+=batchConf.starvePredatorStep)
			{
				for(int bPred=batchConf.breedPredatorFrom;bPred<=batchConf.breedPredatorTo;bPred+=batchConf.breedPredatorStep)
				{
					for(int bPrey=batchConf.breedPreyFrom;bPrey<=batchConf.breedPreyTo;bPrey+=batchConf.breedPreyStep)
					{
						int predWins=0;
						int preyWins=0;
						int unknown=0;
						
						for(int k=0;k<batchConf.numberOfSimulations;k++)
						{
							Tools.updateProgress((prog/total<=100) ? prog/total :100);
							System.out.print("Sim "+k+", point: "+stPred+" "+bPred+" "+bPrey);

							batchConf.updateSimulationParameters(stPred, bPrey, bPred);
							
							Population pop=Simulation.getInstance().getPopulation();
							pop.repopulate();
							Simulation.getInstance().iterate(batchConf.numberOfRounds);
							countAgents();
							int outcome=GlobalConfiguration.winnerRule(predators, preys);
							if(outcome==0)
								unknown++;
							else if(outcome==1)
								predWins++;
							else if(outcome==2)
								preyWins++;
							System.out.print(", Predators: "+predators+" Preys: "+preys+" Outcome: "+outcome);
							prog+=1.0;
						}
						int overAllOutcome=GlobalConfiguration.overallWinnerRule(unknown, predWins, preyWins);
						stream.println(stPred+" "+bPred+" "+bPrey+" "+overAllOutcome);
					}
				}
			}
			System.out.print("\n");
		 }
		catch(FileNotFoundException e)
		{
			e.printStackTrace();
		}
	}
	
	private void saveBatchConfiguration()
	{
		  FileOutputStream fout;
		  try {
				  fout = new FileOutputStream("results/batch/"+batchName+".conf");
				  PrintStream stream=new PrintStream(fout);
				  AgentConfiguration[] confs= AvailableAgents.getInstance().getConfigurations();
				  for(AgentConfiguration conf : confs)
				  {
					String agentType=Tools.extractAgentType(conf.getTemplate().toString());
					if(batchConf.isSelected(agentType)) 
					{
						stream.print(agentType+" "+conf.getInitialPopulationSize());
						stream.println(	"\t\t["+conf.getTemplate().getMovementStrategy().getName()+" "+
											conf.getTemplate().getMovementStrategy().getParameters()+"]");
						
					}
				  }
				  stream.println("rounds : "+batchConf.numberOfRounds);
				  stream.println("simulations : "+batchConf.numberOfSimulations);	
				  stream.close();
		  } catch (FileNotFoundException e) {
				e.printStackTrace();
	      }

	}
	
	private void countAgents()
	{
		predators=0;
		preys=0;
		for(Agent agent : Simulation.getInstance().getPopulation())
		{
			if((agent.getSpeciesName()+"Agent").equals(batchConf.predator))
				predators++;
			else if ((agent.getSpeciesName()+"Agent").equals(batchConf.prey))
				preys++;
		}
		
	}
	
	@Override
	public void update(Observable simulation, Object population) {

	}

	
}
